//////////////////////////////////////////////////////////////////////////
//
// This file is a part of APNS provider project.
// Copyright (c) Vladimir Gumenuk, 2013-2014.
//
// PROJECT:         APNS provider
// FILE:            SimpleThread.cpp: implementation of the CSimpleThread class.
// AUTHOR:          Vladimir Gumenuk
// DATE:            25-Dec-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SimpleThread.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSimpleThread::CSimpleThread()
{
    m_bIsRunning = FALSE;
    m_hThread = NULL;
    m_dwThreadID = 0;
    m_bThreadResult = FALSE;
    m_hWaitEvent = NULL;
    m_runCS.Init();
}

CSimpleThread::~CSimpleThread()
{
    if( m_hWaitEvent )
    {
        ::CloseHandle( m_hWaitEvent );
        m_hWaitEvent = NULL;
    }
    m_runCS.Term();
}

HRESULT CSimpleThread::StartThread()
{
    if( m_bIsRunning )
        return S_FALSE;
    
    m_hWaitEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL );
    if( !m_hWaitEvent )
        return E_FAIL;
    
    ::ResetEvent( m_hWaitEvent );
    
    m_hThread = ::CreateThread( NULL, 0, &StaticThreadProc, this, 0, &m_dwThreadID );
    if( !m_hThread )
    {
        return E_OUTOFMEMORY;
    }
    
    DWORD dwWaitRes = ::WaitForSingleObject( m_hWaitEvent, 20000 );
    if( WAIT_OBJECT_0 != dwWaitRes ||
        !m_bThreadResult )
    {
        ::CloseHandle( m_hThread );
        m_hThread = NULL;
        m_dwThreadID = 0;
        return E_FAIL;
    }
    
    return S_OK;
}

void CSimpleThread::StopThread()
{
    DWORD dwThreadID = m_dwThreadID;

    ATLTRACE( _T("CSimpleThread::StopThread() ThreadID ==  %d start\n"), dwThreadID );

    if( IsRunning() && m_hThread ) // if still running
    {
        if( ::PostThreadMessage( m_dwThreadID, WM_QUIT, 0, 0 ) )
        {
            DWORD dwWait = 5000;
#ifdef _DEBUG
            dwWait = INFINITE;
#endif 
            if( WaitForSingleObject( m_hThread, dwWait ) != WAIT_OBJECT_0 )
            {
                ::TerminateThread( m_hThread, 0 );
                ATLTRACE( _T( "CSimpleThread::StopThread() after  WaitForSingleObject( m_hWaitEvent, dwWait ) \n") );
            }
            ::CloseHandle( m_hThread );
        }

        m_hThread = NULL;
        m_dwThreadID = 0;
    }

    if( m_hThread ) // thread can exit automatically, just close handle
    {
        ATLTRACE( _T( "CSimpleThread::StopThread(): thread can exit automatically, just close handle \n") );
        ::CloseHandle( m_hThread );
        m_hThread = NULL;
        m_dwThreadID = 0;
    }

    if( m_hWaitEvent )
    {
        ::CloseHandle( m_hWaitEvent );
        m_hWaitEvent = NULL;
    }
    ATLTRACE( _T("CSimpleThread::StopThread() 0x%x end\n"), dwThreadID );
}

DWORD __stdcall CSimpleThread::StaticThreadProc( void *pParam )
{
    DWORD dwThreadId = GetCurrentThreadId();
    ATLTRACE( _T("CSimpleThread::StaticThreadProc Thread start - OK ThreadID == id:[%d]  \n"), dwThreadId );
    CSimpleThread *pThread = (CSimpleThread *)pParam;
    
    bool bRes = pThread->InitInstance();
    if( bRes )
    {
        pThread->SetRunning( true );
        pThread->SetThreadEvent( bRes ); // notify, that initialization was successful

        pThread->Run();
        pThread->ExitInstance();
    }
    
    ATLTRACE( _T("CSimpleThread exited - OK id:[%d]!\n"), dwThreadId );
    
    pThread->SetRunning( false );
    pThread->SetThreadEvent( bRes );
    return 0;
}

void CSimpleThread::Run()
{
    MSG msg;
    ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE );
    ATLTRACE(_T(" CSimpleThread::Run() ThreadId == [ %d ] \n "), GetCurrentThreadId() );
    while( ::GetMessage( &msg, NULL, 0, 0 ) )
    {
        ::TranslateMessage( &msg );
        ::DispatchMessage( &msg );
    }
}

void CSimpleThread::SetThreadEvent( bool bResult )
{
    m_bThreadResult = bResult;
    ::SetEvent( m_hWaitEvent );
}

BOOL CSimpleThread::PostMessage( UINT iMsg, WPARAM wParam, LPARAM lParam )
{
    if( m_hThread && m_dwThreadID )
    {
        return ::PostThreadMessage( m_dwThreadID, iMsg, wParam, lParam );
    }
    return FALSE;
}

bool CSimpleThread::IsRunning()
{
    bool bRun;
    m_runCS.Lock();
#   ifndef NO_DUMMY_TRACE
    ATLTRACE(_T(" CSimpleThread::IsRunning(). Checker thread ThreadID == [ %d ] \n"),GetCurrentThreadId() );
#   endif
    bRun = m_bIsRunning;
    m_runCS.Unlock();
    return bRun;
}

void CSimpleThread::SetRunning( bool bRun ) 
{
    m_runCS.Lock();
    m_bIsRunning = bRun;
    m_runCS.Unlock();
}
